#include <iostream>
#include <algorithm>
#include "lsystem.hpp"
#include "../turtle.hpp"
#include "../utils.hpp"

using namespace std;


LSysRule::LSysRule(void):
    from(0), to("")
{
}


LSysRule::LSysRule(char from, const char *to):
    from(from), to(to)
{
}


LSysRule::LSysRule(char from, const string &to):
    from(from), to(to)
{
}


LSystem::LSystem(void):
    age(0), step(0.0), angle(0.0), axiom("")
{
}


LSystem::LSystem(int age, double step, double angle,
                     string axiom, vector<LSysRule> &rules):
    age(age), step(step), angle(angle),
    axiom(axiom), rules(rules)
{
}


int LSystem::vertex_num(void)
{
    string render_str;
    _gen_str(render_str);
    return count(render_str.begin(), render_str.end(), 'F')*2;
}


void LSystem::render(FVertexArray<FVector2d > &va)
{
    string render_str;
    _gen_str(render_str);
    Turtle t;
    for (string::iterator c = render_str.begin(); c != render_str.end(); c++)
        switch (*c) {
        case 'F':
            va.push_back(t.state.pos);
            t.forward(step);
            va.push_back(t.state.pos);
            break;
        case 'f':
            t.forward(step);
            break;
        case '+':
            t.turn_left(angle);
            break;
        case '-':
            t.turn_right(angle);
            break;
        case '[':
            t.remember();
            step -= 0.1;
            break;
        case ']':
            t.restore();
            step += 0.1;
            break;
        }
}


void LSystem::_gen_str(string &dest)
{
    string to_replace;
    for (unsigned int i = 0; i < rules.size(); i++)
        to_replace += rules[i].from;

    dest = axiom;
    
    for (int i = 0; i < age; i++) {
        unsigned int pos = dest.find_first_of(to_replace, 0);
        while (pos != string::npos) {
            int rule_id = _find_rule_for(dest[pos]);
            LSysRule &r = rules[rule_id];
            dest.replace(pos, 1, r.to);
            pos += r.to.length();
            pos = dest.find_first_of(to_replace, pos);
        }
    }
}


int LSystem::_find_rule_for(char c)
{
    for (unsigned i = 0; i < rules.size(); i++)
        if (rules[i].from == c)
            return i;
    return -1;
}
